using System;
using System.Collections.Generic;
using System.Text;

namespace UMC.WorkFlow
{
    /// <summary>
    /// 作业算法引擎
    /// </summary>
    sealed class TaskEngine
    {
        TaskRuntime taskRuntime;
        public TaskEngine(TaskRuntime runtime)
        {
            taskRuntime = runtime;
        }
        public static bool Security(Identity host, Identity guest, ulong taskId)
        {
            if (host.Username == guest.Username)
            {
                return true;
            }
            return host.Proxy(guest, taskId);
        }
        EventInstance taskEvent = new EventInstance();
        void AppendRegister(params RespondTo[] raises)
        {
            for (int i = 0; i < raises.Length; i++)
            {
                raises[i](taskEvent);
            }
        }
        void AppendRegister(params IResponder[] register)
        {
            for (int i = 0; i < register.Length; i++)
            {
                register[i].OnRespond(taskEvent);
            }
        }
        /// <summary>
        /// 寻找驳回人
        /// </summary>
        /// <param name="shape"></param>
        /// <returns></returns>
        bool FindRejectAndIdentity(Shape shape)
        {
            if (shape.Status == ShapeStatus.Rejected)
            {
                if (String.Equals(shape.Identity.Username, this._Identity.Username, StringComparison.CurrentCultureIgnoreCase))
                {
                    return true;
                }
                else
                {
                    return shape.Identity.Proxy(this._Identity, this.TaskId);
                }
            }
            return false;
        }
        bool FindActiveAndIdentity(Shape shape)
        {
            if (shape.Status == ShapeStatus.Activation)
            {
                if (String.Equals(shape.Identity.Username, this._Identity.Username, StringComparison.CurrentCultureIgnoreCase))
                {
                    return true;
                }
                else
                {
                    return shape.Identity.Proxy(this._Identity, this.TaskId);
                }
            }
            return false;
        }
        bool FindLockedAndIdentity(Shape shape)
        {
            if (shape.Status == ShapeStatus.Locked)
            {
                if (String.Equals(shape.Identity.Username, this._Identity.Username, StringComparison.CurrentCultureIgnoreCase))
                {
                    return true;
                }
                else
                {
                    return shape.Identity.Proxy(this._Identity, this.TaskId);
                }
            }
            return false;
        }
        void RegisterEvent(object taskObj)
        {
            if (taskObj is IResponder)
            {
                ((IResponder)taskObj).OnRespond(taskEvent);
            }
        }
        InstanceEventArgs eventArgs;
        ulong TaskId;
        Identity _Identity;

        public Identity Identity
        {
            get
            {
                return _Identity;
            }
        }

        public bool Progress(ulong instanceId, Identity identity, TaskInstance config, params RespondTo[] raises)
        {
            config.InstanceId = instanceId;
            config.StartTime = DateTime.Now;
            config.Status = TaskStatus.Prepare;
            config.Identity = identity;
            foreach (Shape shape in config.Shapes)
            {
                shape.Status = ShapeStatus.Unactivated;
            }
            foreach (Line lne in config.Lines)
            {
                lne.Status = LineStatus.UnPass;
            }
            return this.Progress(config, identity, CommandType.Submit, raises);
        }
        public bool Progress(TaskInstance task, Identity identity, CommandType cmdKey, params RespondTo[] raises)
        {
            this.TaskId = task.TaskId;
            this._Identity = identity;
            Shape shape;
            AppendRegister(raises);
            AppendRegister(task.Registes.ToArray());
            switch (task.Status)
            {
                case TaskStatus.Completed:
                    return false;
                case TaskStatus.Prepare:
                    if (cmdKey == CommandType.Submit)
                    {
                        if (Security(task.Identity, identity, task.TaskId))
                        {
                            task.Status = TaskStatus.InReview;
                            shape = GetStartShape(task);
                            shape.Status = ShapeStatus.Approved;
                            shape.Identity = task.Identity;
                            RegisterEvent(shape);

                            eventArgs = new InstanceEventArgs(cmdKey, this._Identity, shape, taskRuntime);

                            taskEvent.RaiseEvent("OnBeginProcess", task, this.eventArgs);
                            shape.Activation(null, task, this.taskRuntime);
                            this.Algorithm(task, shape);

                            taskEvent.RaiseEvent("OnSubmitted", task, this.eventArgs);
                        }
                        else
                        {
                            throw new SecurityException(task.Status, cmdKey);
                        }
                    }
                    else
                    {
                        throw new SecurityException(task.Status, cmdKey);
                    }
                    break;
                case TaskStatus.Termination:
                    /*                         
                     *在流程在Termination中操作有下几中情况
                     * 一、被申请人再次提交，
                     * 二、原Reject的操作人再次通过
                     */
                    switch (cmdKey)
                    {
                        case CommandType.Submit:
                            if (Security(task.Identity, identity, task.TaskId))
                            {
                                task.Status = TaskStatus.InReview;
                                shape = GetStartShape(task);
                                shape.Identity = task.Identity;
                                RegisterEvent(shape);

                                eventArgs = new InstanceEventArgs(cmdKey, this._Identity, shape, taskRuntime);
                                shape.Status = ShapeStatus.Approved;
                                taskEvent.RaiseEvent("OnBeginProcess", task, this.eventArgs);
                                shape.Activation(null, task, this.taskRuntime);
                                this.Algorithm(task, shape);
                                taskEvent.RaiseEvent("OnSubmitted", task, this.eventArgs);
                                // this.
                            }
                            else
                            {
                                throw new SecurityException(task.Status, cmdKey);
                            }
                            break;
                        case CommandType.Approval:
                            shape = task.Shapes.Find(this.FindRejectAndIdentity);//FindAcitivationAndIdentity);
                            if (shape == null)
                            {
                                throw new SecurityException(task.Status, cmdKey);
                            }
                            RegisterEvent(shape);
                            eventArgs = new InstanceEventArgs(cmdKey, this._Identity, shape, taskRuntime);
                            taskEvent.RaiseEvent("OnBeginProcess", task, eventArgs);

                            shape.Status = ShapeStatus.Approved;
                            task.Status = TaskStatus.InReview;
                            Algorithm(task, shape);
                            this.taskEvent.RaiseEvent("OnApproved", task, eventArgs);
                            break;
                        default:
                            throw new SecurityException(task.Status, cmdKey);
                    }
                    break;
                case TaskStatus.Locked:
                    /*
                     * 在Waitting状态下有下列情况
                     * 一、审批通过;
                     * 二、驳回;
                     * 三、其他为非法审批
                     */
                    shape = task.Shapes.Find(this.FindLockedAndIdentity);
                    if (shape != null)
                    {
                        RegisterEvent(shape);
                        eventArgs = new InstanceEventArgs(cmdKey, this._Identity, shape, taskRuntime);
                        taskEvent.RaiseEvent("OnBeginProcess", task, eventArgs);
                        if (cmdKey == CommandType.Apostille)
                        {
                            shape.Status = ShapeStatus.Approved;
                            task.Status = TaskStatus.InReview;
                            var name = taskRuntime.Items["UI.Apostille"] as string;
                            if (String.IsNullOrEmpty(name) == false)
                            {
                                UMC.WorkFlow.Subassembly.Shape.AppendShape(task, shape, true, name.Split(',', ' '));
                            }

                            Algorithm(task, shape);
                            this.taskEvent.RaiseEvent("OnApproved", task, eventArgs);
                        }
                        else if (cmdKey == CommandType.Approval)
                        {
                            shape.Status = ShapeStatus.Approved;
                            task.Status = TaskStatus.InReview;
                            Algorithm(task, shape);
                            this.taskEvent.RaiseEvent("OnApproved", task, eventArgs);
                        }
                        else if (cmdKey == CommandType.Reject)
                        {
                            shape.Status = ShapeStatus.Rejected;
                            task.Status = TaskStatus.Termination;
                            taskEvent.RaiseEvent("OnRejected", task, eventArgs);
                        }
                        else
                        {
                            throw new SecurityException(task.Status, cmdKey);
                        }
                    }
                    else
                    {
                        throw new SecurityException(task.Status, cmdKey);
                    }
                    break;
                case TaskStatus.InReview:
                    /*
                     * 在Inreview中有下几种操作
                     * 一、Approval，
                     * 二、进行了Reject了
                     * 三、进行了Waitting
                     */
                    shape = task.Shapes.Find(this.FindActiveAndIdentity);
                    if (shape != null)
                    {
                        RegisterEvent(shape);
                        eventArgs = new InstanceEventArgs(cmdKey, this._Identity, shape, taskRuntime);
                        taskEvent.RaiseEvent("OnBeginProcess", task, eventArgs);

                        switch (cmdKey)
                        {
                            case CommandType.Apostille:
                                shape.Status = ShapeStatus.Approved;
                                task.Status = TaskStatus.InReview;

                                var name = taskRuntime.Items["UI.Apostille"] as string;
                                if (String.IsNullOrEmpty(name) == false)
                                {
                                    UMC.WorkFlow.Subassembly.Shape.AppendShape(task, shape, true, name.Split(',', ' '));
                                }
                                Algorithm(task, shape);
                                this.taskEvent.RaiseEvent("OnApproved", task, eventArgs);
                                break;

                            case CommandType.Approval:
                                shape.Status = ShapeStatus.Approved;
                                task.Status = TaskStatus.InReview;
                                Algorithm(task, shape);
                                this.taskEvent.RaiseEvent("OnApproved", task, eventArgs);
                                break;
                            case CommandType.Reject:
                                var RejectTo = taskRuntime.Items["UI.Reject.To"] as string;
                                shape.Status = ShapeStatus.Rejected;
                                if (String.IsNullOrEmpty(RejectTo) == false)
                                {
                                    var ls = task.Induces.Find(iLne => iLne.FromId == shape.Id && iLne.ToId == RejectTo);
                                    if (ls != null)
                                    {
                                        task.Status = TaskStatus.InReview;
                                        taskEvent.RaiseEvent("OnRejected", task, eventArgs);

                                        RegisterEvent(ls);
                                        this.Goto(task, ls.ToId);
                                    }
                                    else
                                    {

                                        task.Status = TaskStatus.Termination;
                                        taskEvent.RaiseEvent("OnRejected", task, eventArgs);
                                    }
                                }
                                else
                                {

                                    task.Status = TaskStatus.Termination;
                                    taskEvent.RaiseEvent("OnRejected", task, eventArgs);

                                    var ls = task.Induces.FindAll(iLne => iLne.FromId == shape.Id);
                                    if (ls.Count == 1)
                                    {
                                        task.Status = TaskStatus.InReview;
                                        taskEvent.RaiseEvent("OnRejected", task, eventArgs);

                                        RegisterEvent(ls[0]);
                                        this.Goto(task, ls[0].ToId);
                                    }
                                }
                                //
                                break;
                            case CommandType.Lock:
                                shape.Status = ShapeStatus.Locked;
                                task.Status = TaskStatus.Locked;
                                // router.ProcessSpring(vector, task);
                                taskEvent.RaiseEvent("OnLocked", task, eventArgs);
                                //if (OnLocked != null) OnLocked(task, processEvent);

                                break;
                            case CommandType.Terminate:
                                //既是审批人又是申请者
                                if (Security(task.Identity, identity, task.TaskId))
                                {
                                    shape.Status = ShapeStatus.Rejected;
                                    task.Status = TaskStatus.Termination;
                                    taskEvent.RaiseEvent("OnRejected", task, eventArgs);
                                    //if (OnRejected != null) OnRejected(task, processEvent);
                                }
                                else
                                {
                                    throw new SecurityException(task.Status, cmdKey);
                                }
                                break;
                            default:
                                throw new SecurityException(task.Status, cmdKey);

                        }
                    }
                    else if (cmdKey == CommandType.Terminate)
                    {
                        /*
                         * Reject还有一种操作，　就是创建人自己终止的
                         * 
                         * 
                         */
                        if (Security(task.Identity, identity, task.TaskId))
                        {
                            shape = GetStartShape(task);
                            RegisterEvent(shape);
                            shape.Status = ShapeStatus.Terminated;
                            task.Status = TaskStatus.Termination;
                            eventArgs = new InstanceEventArgs(cmdKey, this._Identity, shape, taskRuntime);
                            taskEvent.RaiseEvent("OnBeginProcess", task, eventArgs);
                            taskEvent.RaiseEvent("OnRejected", task, eventArgs);
                        }
                        else
                        {
                            throw new SecurityException(task.Status, cmdKey);
                        }
                    }
                    else
                    {
                        throw new SecurityException(task.Status, cmdKey);
                    }
                    break;
            }

            /*
             * 在触发一系列事件之后，对当前流程的改变是不生效;
             */
            OnEndProcess(task);
            // taskEvent.RaiseEvent("OnEndProcess", task, processEvent);
            //if (OnEndProcess != null) OnEndProcess(task, processEvent);
            return true;

        }
        private static Shape GetStartShape(TaskInstance task)
        {
            List<string> logs = new List<string>();
            for (int i = 0; i < task.Shapes.Count; i++)
            {
                if (!task.Lines.Exists(lne => lne.ToId == task.Shapes[i].Id))
                {
                    return task.Shapes[i];
                }
            }
            throw new NotFindShapeException("Start");
        }
        /// <summary>
        /// 完成线的逻辑
        /// </summary>
        /// <param name="task"></param>
        /// <param name="line"></param>
        void Algorithm(TaskInstance task, Line line)
        {
            Shape shape = task.Shapes.Find(sp => sp.Id == line.ToId);
            if (line.Status == LineStatus.Destructive)
            {
                Effectivene(task, line);
            }
            else
            {
                line.Status = LineStatus.Passed;
                shape.Activing(line, task, taskRuntime);
                if (!task.Lines.Exists(lne =>
                     {
                         return lne.ToId == shape.Id && lne.Status == LineStatus.UnPass;
                         //  return reBool;
                     }))
                {
                    shape.Status = ShapeStatus.Activation;
                    shape.Activation(line, task, this.taskRuntime);
                    if (shape.Identity == null || String.IsNullOrEmpty(shape.Identity.Username))
                    {
                        Effectivene(task, shape);
                    }
                    else
                    {
                        taskEvent.RaiseEvent("OnPassLayer", task, eventArgs);
                    }
                }
            }


        }
        /// <summary>
        /// 完成图形点逻辑
        /// </summary>
        /// <param name="task"></param>
        /// <param name="shape"></param>
        void Algorithm(TaskInstance task, Shape shape)
        {
            List<Line> lLine = task.Lines.FindAll(line => line.FromId == shape.Id);

            foreach (Line lne in lLine)
            {

                lne.Activing(shape, task, this.taskRuntime);
                Algorithm(task, lne);
            }

        }
        /// <summary>
        /// 完成线失效逻辑
        /// </summary>
        /// <param name="task"></param>
        /// <param name="line"></param>
        /// <remarks>因为是失效再进行的图形的激活，故不调用图形的Activing方法</remarks>
        void Effectivene(TaskInstance task, Line line)
        {
            Shape shape = task.Shapes.Find(sp => sp.Id == line.ToId);


            List<Line> lLine = task.Lines.FindAll(lne => lne.ToId == shape.Id);

            if (!lLine.Exists(lne => lne.Status == LineStatus.UnPass || lne.Status == LineStatus.Passed))
            {
                shape.Status = ShapeStatus.Unworking;
                EffectiveneByLine(task, shape);

            }
            else if (!lLine.Exists(lne => lne.Status == LineStatus.UnPass)
            )
            {
                shape.Status = ShapeStatus.Activation;
                shape.Activation(line, task, this.taskRuntime);
            }
        }
        void EffectiveneByLine(TaskInstance task, Shape shape)
        {
            shape.Status = ShapeStatus.Unworking;
            List<Line> lLine = task.Lines.FindAll(line => line.FromId == shape.Id);
            foreach (Line lne in lLine)
            {
                lne.Status = LineStatus.Destructive;
                Effectivene(task, lne);
            }
        }
        /// <summary>
        /// 完成图形点逻辑
        /// </summary>
        /// <param name="task"></param>
        /// <param name="shape"></param>
        void Effectivene(TaskInstance task, Shape shape)
        {
            shape.Status = ShapeStatus.Unworking;
            List<Line> lLine = task.Lines.FindAll(line => line.FromId == shape.Id);
            foreach (Line lne in lLine)
            {
                lne.Activing(this.eventArgs.Shape, task, this.taskRuntime);
                Algorithm(task, lne);
            }

        }

        void InduceLine(TaskInstance task)
        {
            var ls = task.Induces.FindAll(iLne =>
            {
                bool IsCan = iLne.Status == eventArgs.CommandType;
                if (IsCan)
                    IsCan = iLne.FromId == eventArgs.Shape.Id;
                return IsCan;
            });
            foreach (InduceLine trig in ls)
            {
                RegisterEvent(trig);
                task.Status = TaskStatus.InReview;
                this.Goto(task, trig.ToId);
            }
        }

        void OnEndProcess(TaskInstance task)
        {
            // InduceLine(task);
            if (!task.Shapes.Exists(she =>
                 {
                     /*
                                         * 最后检查是不是不存在激活和挂起，和驳回路线
                                         * 在各种事件处理之后，当前流程有下几咱状态
                                         * 一、路线不存在挂起，驳回也不存在激活，则当前流程状态是完成的，则触发OnCompleted事件，并做完成处理工作
                                         * 二、所有路线有挂起，激活，驳回，路线状态之一，则流程没有完成，并做末完成处理工作
                 		                             
                 		                             
                                         */
                     return she.Status == ShapeStatus.Locked || she.Status == ShapeStatus.Rejected || she.Status == ShapeStatus.Activation;
                 }))
            {
                task.Status = TaskStatus.Completed;
                task.FinishTime = DateTime.Now;
                taskEvent.RaiseEvent("OnCompleted", task, eventArgs);
                //if (OnCompleted != null) OnCompleted(task, processEvent);
            }
            taskEvent.RaiseEvent("OnEndProcess", task, eventArgs);

        }
        /// <summary>
        /// ID中是否包含复合ID
        /// </summary>
        /// <param name="pid"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        static bool ComplexPredicate(string pid, string id)
        {

            if (pid.Length < id.Length)
            {
                if (id[pid.Length] == '-')
                {
                    if (id.StartsWith(pid))
                    {
                        return true;
                    }
                }
            }
            return (pid == id);
        }
        //static void Initialize(object obj, TaskInstance task)
        //{
        //    if (obj is IInitialize)
        //    {
        //        ((IInitialize)obj).Initialize(task);
        //    }
        //}
        /// <summary>
        /// 数组批量处理
        /// </summary>
        /// <typeparam name="T">数组类型</typeparam>
        /// <param name="args">数组</param>
        /// <param name="action">处理方法</param>
        /// <returns></returns>
        static void Each<T>(T[] args, System.Action<T> action)
        {
            for (int i = 0; i < args.Length; i++)
            {
                action(args[i]);
            }
        }
        /// <summary>
        /// 前调
        /// </summary>
        static void RollByBegin(TaskInstance task, string shapeId)
        {

            foreach (Line line in task.Lines)
            {
                if (line.ToId == shapeId)
                {
                    Shape[] shapes = task.Shapes.FindAll(shpe => line.FromId == shpe.Id).ToArray();

                    Each<Shape>(shapes, sh =>
                    {
                        if (sh.Status == ShapeStatus.Unactivated || sh.Status == ShapeStatus.Activation)
                            sh.Status = ShapeStatus.Unworking;
                    });
                    if (line.Status == LineStatus.UnPass)
                        line.Status = LineStatus.Destructive;

                    RollByBegin(task, line.FromId);
                }
            }
        }
        //回调
        static void RollByEnd(TaskInstance task, string shapeId)
        {

            foreach (Line line in task.Lines)
            {
                if (line.FromId == shapeId)
                {
                    Shape[] shapes = task.Shapes.FindAll(shpe => (line.ToId == shpe.Id)).ToArray();
                    Each<Shape>(shapes, sh => sh.Status = ShapeStatus.Unactivated);
                    line.Status = LineStatus.UnPass;
                    RollByEnd(task, line.ToId);
                }
            }
        }

        /// <summary>
        /// 转到指定的逻辑点
        /// </summary>
        /// <remarks>如果逻辑点中有最后完成结点，则触发OnCompleted事件</remarks>
        /// <param name="task">工作流</param> Goto
        /// <param name="shapes">逻辑点集合</param>
        internal bool Goto(TaskInstance task, params string[] shapes)
        {
            foreach (string shapeid in shapes)
            {

                Shape shape = task.Shapes.Find(shpe => shpe.Id == shapeid);
                //RollByBegin(task, shapeid);
                //RollByEnd(task, shapeid);
                if (shape != null)
                {
                    RollByBegin(task, shapeid);
                    RollByEnd(task, shapeid);
                    shape.Status = ShapeStatus.Activation;
                    shape.Activation(null, task, this.taskRuntime);

                    if (String.IsNullOrEmpty(shape.Identity.Username))
                    {
                        Effectivene(task, shape);
                    }
                }
                else
                {
                    throw new NotFindShapeException(shapeid);
                }
            }
            return true;

        }



    }
}
